/*
 * Copyright 2007 - The JDPF Project Team (http://www.jdpf.org) 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package org.jdpf.core.kernel.elements;

import java.util.ArrayList;
import java.util.List;

import org.jdpf.core.kernel.exceptions.CoreException;
import org.jdpf.core.kernel.exceptions.CoreExceptionsEnum;
import org.jdpf.core.plugins.blocks.transitions.OccurrenceConstraints;

/**
 * Represents the generic net <em>node</em>.
 * 
 * @author Paolo Ciccarese
 */
public class AbstractNode extends AbstractNetElement implements IWiring {

	private final OccurrenceConstraints occurrence;
	
	protected List<InputArc> inputs = new ArrayList<InputArc>(10);
	protected List<OutputArc> outputs = new ArrayList<OutputArc>(10);
	
	/**
	 * Full constructor.
	 * @param id			The unique id of the transition
	 * @param name			The name of the transition
	 * @param minInputArcs	The minimun number of input arcs
	 * @param maxInputArcs	The maximum number of input arcs
	 * @param minOutputArcs The minimun number of output arcs
	 * @param maxOutputArcs The maximum number of output arcs
	 */
	public AbstractNode(final String id, final String name, OccurrenceConstraints occurrence) {
		super(id, name);
		
		this.occurrence = occurrence;
	}	
	
	/**
	 * Checks if the blocks are well wired with the rest of the network.
	 */
	public void checkWiringValidity() throws CoreException {
		// TODO Wiring checking to be transformed into logging?
		if (inputs.size() < occurrence.getMinInputArcs())
			throw new CoreException(CoreExceptionsEnum.TooFewInputArcs, 
					buildMessage("Too few input arcs"));
		if (inputs.size() > occurrence.getMaxInputArcs())
			throw new CoreException(CoreExceptionsEnum.TooManyInputArcs, 
					buildMessage("Too many input arcs"));	
		if (outputs.size() < occurrence.getMinOutputArcs())
			throw new CoreException(CoreExceptionsEnum.TooFewOutputArcs, 
					buildMessage("Too few output arcs"));
		if (outputs.size() > occurrence.getMaxOutputArcs())
			throw new CoreException(CoreExceptionsEnum.TooManyOutputArcs, 
					buildMessage("Too many output arcs"));
	}
	
	private String buildMessage(String message) {
		StringBuilder sb = new StringBuilder();
		
		sb.append("Node with id: ").append(super.getId());
		sb.append(" and name: ").append(super.getName()).append("\n");
		
		sb.append("Message: ").append(message).append("!\n");
		
		sb.append("Min/Max input arcs allowed ").append(occurrence.getMinInputArcs());
		sb.append("/").append(occurrence.getMaxInputArcs()).append(" - inputs: ");
		sb.append(inputs.size()).append("\n");
		
		sb.append("Min/Max output arcs allowed ").append(occurrence.getMinOutputArcs());
		sb.append("/").append(occurrence.getMaxOutputArcs()).append(" - outputs: ");
		sb.append(outputs.size()).append("\n");
		
		return sb.toString();
	}
	
	public AbstractNode addInputArc(final InputArc inputArc) {
		if (inputs.size() > occurrence.getMaxInputArcs()-1)
			new CoreException(CoreExceptionsEnum.TooManyInputArcs, 
					"Too many input arcs for the Transition with id: "
							+ super.getId() + " and name: " + super.getName());

		inputs.add(inputArc);
		return this;
	}

	public AbstractNode addOutputArc(final OutputArc outputArc) {
		if (outputs.size() > occurrence.getMaxOutputArcs()-1)
			new CoreException(CoreExceptionsEnum.TooManyOutputArcs, 
					"Too many output arcs for the Transition with id: "
							+ super.getId() + " and name: " + super.getName());

		outputs.add(outputArc);
		return this;
	}

	public List<InputArc> getInputArcs() {
		return inputs;
	}

	public List<OutputArc> getOutputArcs() {
		return outputs;
	}

	public AbstractNode removeInputArc(final InputArc inputArc) {
		this.inputs.remove(inputArc);
		return this;
	}

	public AbstractNode removeOutputArc(final OutputArc outputArc) {
		this.outputs.remove(outputArc);
		return this;
	}
}
